import direct.directbase.DirectStart
from panda3d.core import GraphicsOutput
from panda3d.core import Filename,Texture
from panda3d.core import CardMaker
from panda3d.core import Point3,Vec3,Vec4,BitMask32
from panda3d.core import NodePath,TextNode
from panda3d.core import AmbientLight,DirectionalLight
from panda3d.core import CollisionTraverser,CollisionNode
from panda3d.core import CollisionHandlerQueue,CollisionRay
from panda3d.core import GeomNode
from direct.showbase.DirectObject import DirectObject
from direct.gui.OnscreenText import OnscreenText
from direct.task.Task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import *
from random import uniform
from math import sqrt,fabs,floor
from pandac.PandaModules import Fog
from direct.gui.DirectGui import *
import sys,os,time,random
from LevelOne import *

map = []
adj = [(-1,-1),(-1,0),(-1,1),
       (0,-1),        (0,1),
       (1,-1), (1,0), (1,1)]
  
class space():
    def __init__(self):
        self.locx = 0
        self.locy = 0
        self.type = "N" #"E" = Empty, "N" = Normal, "B" = Blocked
        self.canReach = False
        self.m = loader.loadModel("models/DungeonTile1")
        self.distFPlayer = 0
        
    def changePos(self,x,y):
        self.locx = x
        self.locy = y
        
    

class player():
    global map
    def __init__(self):
        #RPG system stats
        self.HP = 10
        self.mHP = 10
        self.MP = 10
        self.ST = 10
        self.Speed = 3
        self.Def = 2
        self.Strength = 3
        self.MoveAction = 3
        self.StandardAction = 1
        self.MinorAction = 1
        self.Accuracy = 75
		self.EXP = 0
		self.MaxEXP = 100
        
        
        #Other stats        
        self.locx = 0
        self.locy = 0
        self.m = Actor("models/Teapot.X")
        self.m.setScale(0.1,0.1,0.1)
        self.m.reparentTo(render)
        #print self.m
        
        #Damage Text
        self.dtext = TextNode("DamageText")
        self.dtext.setText("")
        self.dtext.setTextColor(1, 0.5, 0.5, 1)
        textNodePath = self.m.attachNewNode(self.dtext)
        textNodePath.setPos(self.m.getBounds().getCenter())
        textNodePath.setZ(10)
        textNodePath.setScale(100,100,100)
        textNodePath.setBillboardPointWorld()
        
    def move(self,x,y):
        self.m.loop("Teapot")
        self.m.headsUp(map[x][y].m)
        i = LerpPosInterval(self.m,
            duration=.5*sqrt((x - self.locx)**2 + (y - self.locy)**2),
            pos=map[x][y].m.getBounds().getCenter(),            
            startPos=self.m.getPos(),
            other=None,
            blendType = "noBlend",
            bakeInStart = 1,
            fluid = 0,
            name=None)
        self.locx = x
        self.locy = y
        
        map[x][y].m.setTexture(loader.loadTexture("textures/Tile1HV.bmp"),1)   
        walkSe = Sequence(i,
            Func(animStop,self.m))
        walkSe.start()
        if self.MoveAction == 0 and self.StandardAction > 0:
            self.StandardAction -= 1
        else:
            self.MoveAction -= 1
        App.updateGUI(app)
        
        
    def tele(self,x,y):
        global map
        self.locx = x
        self.locy = y
        self.m.setPos (map[x][y].m.getBounds().getCenter())
        
    def takeDamage(self,d):
        if not d - self.Def < 0:
            self.HP -= (d-self.Def)
            #Damage Text -----------
            damagedSe = Sequence(Func(self.printDamage,d - self.Def),
                Wait(0.50),
                Func(self.destroyText))
                
            damagedSe.start()
            App.updateGUI(app)
        else:
            damagedSe = Sequence(Func(self.printDamage,"Miss"),
                Wait(0.50),
                Func(self.destroyText))
                
        damagedSe.start()
        
        if self.HP <= 0:
            Wait(3)
            self.die(d)
            
    def attack(self,x,y,enemies):
        if self.Strength > 0:
            map[x][y].m.setTexture(loader.loadTexture("textures/Tile1AttackSel.bmp"),1)   
            for i in enemies:
                if x == i.locx and y == i.locy:
                    self.m.headsUp(i.m)
                    hitormiss = False
                    closenessMod = 1
                    randy = random.randrange(0,100)
                    
                    if randy <= self.Accuracy:
                        hitormiss = True
                        
                    if hitormiss == True:    
                        closenessMod = floor(self.Accuracy - randy)
                        i.takeDamage (floor(self.Strength + (closenessMod * 0.05)))
                        print floor((self.Strength + (closenessMod * 0.05)))
                    else:
                        i.takeDamage(0)
                        
                    self.StandardAction -= 1
                    App.updateGUI(app)
                
    def printDamage(self,x):
        if x == "Miss":
            self.dtext.setText(str(x))
        else:
            self.dtext.setText(str(int(x)))
        
    def destroyText(self):
        self.dtext.setText("")
        
    
    def die(self,d):
        print "YOU LOSE!!!!!!!!!"
        self.taskMgr.remove
        self.m.removeNode()

	def getEXP(self,a):
		self.EXP += a
		if self.EXP >= self.MaxEXP:
			self.LvlUp(self.EXP - self.MaxEXP)
		App.updateGUI(app)
	
	def LvlUp(self,e):
		self.EXP = 0
		self.MaxEXP *= 2
		self.EXP += e
		if classType == "Warrior":
			self.mHP += 10
			self.Strength += 2
			self.ST += 10
		App.updateGUI(app)
		
		

        
class enemy():
    global map
    def __init__(self):
        #RPG system stats
        self.HP = 10
        self.mHP = 10
        self.HPString = str(self.HP)
        self.MP = 10
        self.ST = 10
        self.Speed = 3
        self.Def = 1
        self.Strength = 3
        self.MoveAction = 1
        self.StandardAction = 1
        self.MinorAction = 1
        self.Accuracy = 75
        self.sightrange = 10
        
        #GUI!
        self.HPBar = DirectWaitBar(value = self.HP,
            range = self.mHP,
            pos = (0,0,50),
            scale = (50,200,200))
        self.HPBar.setBin("fixed",0)
        self.HPBar.setDepthWrite(False)
        
        #Other stats   
        self.locx = 0
        self.locy = 0
        self.m = Actor("models/RustyTeapot.X")
        self.m.setScale(0.1,0.1,0.1)
        self.m.reparentTo(render)
        
        self.HPBar.reparentTo(self.m)
        self.HPBar.setTwoSided(True)
        
        self.dtext = TextNode("DamageText")
        self.dtext.setText("")
        self.dtext.setTextColor(1, 0.5, 0.5, 1)
        textNodePath = self.m.attachNewNode(self.dtext)
        textNodePath.setPos(self.m.getBounds().getCenter())
        textNodePath.setZ(10)
        textNodePath.setScale(100,100,100)
        textNodePath.setBillboardPointWorld()
        textNodePath.setBin("fixed",40)
        textNodePath.setDepthWrite(False)
        
    def move(self,x,y):
        self.m.loop("RustyTeapot")
        self.m.headsUp(map[x][y].m)
        i = LerpPosInterval(self.m,
            duration=.5*sqrt((x - self.locx)**2 + (y - self.locy)**2),
            pos=map[x][y].m.getBounds().getCenter(),            
            startPos=self.m.getPos(),
            other=None,
            blendType = "noBlend",
            bakeInStart = 1,
            fluid = 0,
            name=None)
        self.locx = x
        self.locy = y
        walkSe = Sequence(i,
            Func(animStop,self.m))
        walkSe.start()
        if self.MoveAction == 0 and self.StandardAction > 0:
            self.StandardAction -= 1
        else:
            self.MoveAction -= 1
        
    def tele(self,x,y):
        self.locx = x
        self.locy = y
        self.m.setPos(map[x][y].m.getBounds().getCenter())
        
    def takeDamage(self,d):
        if not d - self.Def <= 0:
            self.HP -= (d-self.Def)
            #Damage Text -----------
            damagedSe = Sequence(Func(self.printDamage,d - self.Def),
                Wait(0.50),
                Func(self.destroyText))
                
            damagedSe.start()
            self.updateBars()
            
        else:
            damagedSe = Sequence(Func(self.printDamage,"Miss"),
                Wait(0.50),
                Func(self.destroyText))
                
            damagedSe.start()
            self.updateBars()
        
        if self.HP <= 0:
            Wait(3)
            self.die(d)
            
    def attack(self,t):
        if self.StandardAction > 0:
            self.m.headsUp(t.m)
            hitormiss = False
            closenessMod = 1
            randy = random.randrange(0,100)
            if randy <= self.Accuracy:
                hitormiss = True
                        
            if hitormiss == True:    
                closenessMod = floor(self.Accuracy - randy)
            
                t.takeDamage (floor(self.Strength + (closenessMod * 0.05)))
            
                print floor((self.Strength + (closenessMod * 0.05)))
            else:
                t.takeDamage(0)
                
            self.StandardAction -= 1
            App.updateGUI(app)
        
    
    def printDamage(self,x):
        if x == "Miss":
            self.dtext.setText(str(x))
        else:
            self.dtext.setText(str(int(x)))
        
    def destroyText(self):
        self.dtext.setText("")
        
    
    def die(self,d):
        damagedSe = Sequence(Func(self.printDamage,d - self.Def),
            Wait(0.50),
            Func(self.destroyText))
        damagedSe.start()
        self.updateBars()
        App.player1.getEXP(self.EXP)
        appthing = getattr(app, "enemies")
        appthing.remove(self)
        appthing2 = getattr(app, "turnList")
        appthing2.remove(self)
        self.m.removeNode()
        
    def updateBars(self):
        self.HPBar.update(self.HP)
        


class App(DirectObject):
    def __init__(self):
        base.disableMouse()
        self.onlyTiles = render.attachNewNode("onlyTiles")
        #self.create_map(10,10)
        self.createLevel1()
        self.player1 = player()
        self.player1.tele(1,23)
        self.player1.m.setH(-150)
        self.turnList = []
        self.currentTurn = 0
        self.enemies = []
        self.camTarget = self.player1
        camera.setPos(15,-66.8,60)
        camera.setHpr(0,-37.1,0)
        self.turnList = [self.player1]
        self.createEnemies()
        #Bool
        self.moveTurn = False
        self.attackTurn = False
        self.enemyTurn = False
        #Textures
        self.normalTex = loader.loadTexture("textures/Tile1.bmp")
        self.mSelTex = loader.loadTexture("textures/Tile1HV.bmp")
        self.mTex = loader.loadTexture("textures/Tile1HL.bmp")
        self.aTex = loader.loadTexture("textures/Tile1Attack.bmp")
        self.aSelTex = loader.loadTexture("textures/Tile1AttackSel.bmp")
		self.HpFillTex = loader.loadTexture("textures/GUITextures/HealthBarFill.PNG")
        self.HpBarTex = loader.loadTexture("textures/GUITextures/HealthBarBackround.PNG")
        #GUI------------------------------------------------------
        self.actionFrame = DirectFrame(frameColor=(0,255,255,0.3),
            frameSize=(-0.4,0.3,-0.2,0.3),
            pos=(1.035,0,-0.799))
            
        self.statusFrame = DirectFrame(frameColor=(0,255,255,0.3),
            frameSize=(-1.67,0.3,-0.2,0.3),
            pos=(0.34,0,-0.799))
            
        self.mButton = DirectButton(text = "Move",
            pos=(0.765,0,-0.58),scale=(0.1,0.1,0.1),command = self.moveClicked)
        self.aButton = DirectButton(text = "Attack",
            pos=(1.17,0,-0.58),scale=(0.1,0.1,0.1),command = self.attackClicked)
        
        self.pButton = DirectButton(text = "End Turn",
            pos=(0.85,0,-0.975),scale=(0.1,0.1,0.1),command = self.endTurn)
            
        self.playerHP = DirectWaitBar(value = self.player1.HP,
            range = self.player1.mHP,
            pos = (-0.7,0,-0.75),
            scale = (0.3,0.5,0.3))
            
        self.moveActionText = OnscreenText(
            text="Move actions left: " + str(self.player1.MoveAction),
            style=1, 
            fg=(1,1,1,1), 
            pos=(0.2,-0.78), 
            scale = .08
        )
        
        self.standardActionText = OnscreenText(
            text="Standard actions left: " + str(self.player1.StandardAction),
            style=1, 
            fg=(1,1,1,1), 
            pos=(0.14,-0.6), 
            scale = .08
        )
        
        self.minorActionText = OnscreenText(
            text="Minor actions left: " + str(self.player1.MinorAction),
            style=1, 
            fg=(1,1,1,1), 
            pos=(0.2,-0.93), 
            scale = .08
        )
        
        #Mouse Collision Stuff----------------------------
        self.picker = CollisionTraverser()            #Make a traverser
        self.pq = CollisionHandlerQueue()  
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.picker.addCollider(self.pickerNP, self.pq)
        
        self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')
        self.updateCam = taskMgr.add(self.updateCam, "updateCam")
        base.camera.setPos(0,-750,750)
        self.selSquare = None
        self.hovSquare = None
        self.accept("escape", sys.exit, [0])
        self.accept("e", self.printText)
        self.accept("mouse1", self.selSpace)
        self.accept("mouse3", self.printLoc)
        base.setBackgroundColor(0,0,0)    #Set the background to black
        
    
    
    def createEnemies(self):
        global map
        #CreateEnemies for lvl1
        enemy1 = enemy()
        enemy1.tele(4,18)
        self.enemies.append(enemy1)
        self.turnList.append(enemy1)
        """enemy2 = enemy()
        enemy3 = enemy()
        enemy4 = enemy()
        enemy5 = enemy()
        enemy6 = enemy()
        enemy7 = enemy()
        """
        """count = 0
        while count != n:
            enemy1 = enemy()
            randy1 = random.randrange(1,24)
            randy2 = random.randrange(1,24)
            enemy1.tele(randy1,randy2)
            self.enemies.append(enemy1)
            self.turnList.append(enemy1)
            for i in self.enemies:
                if i.locx == enemy1.locx and i.locy == enemy1.locy:
                    enemy1.tele(enemy1.locx + 1, enemy1.locy + 1)
                if enemy1.locx == self.player1.locx and self.enemy1.locy == player1.locy:
                    enemy1.tele(enemy1.locx + 1, enemy1.locy + 1)
            count += 1
           """ 
    def updateCam(self,task):
        base.camera.lookAt(self.camTarget.m)
        base.camera.reparentTo(self.camTarget.m)
        #base.camera.setPos(0,-750,750)
        return task.cont
    
    def printText(self):
        print camera.getPos()
        print camera.getHpr()
    
    def updateGUI(self):
        self.playerHP.update(self.player1.HP)
        self.moveActionText.destroy()
        self.moveActionText = OnscreenText(
            text="Move actions left: " + str(self.player1.MoveAction),
            style=1, 
            fg=(1,1,1,1), 
            pos=(0.2,-0.78), 
            scale = .08
        )
        self.standardActionText.destroy()
        self.standardActionText = OnscreenText(
            text="Standard actions left: " + str(self.player1.StandardAction),
            style=1, 
            fg=(1,1,1,1), 
            pos=(0.14,-0.6), 
            scale = .08
        )
        self.minorActionText.destroy()
        self.minorActionText = OnscreenText(
            text="Minor actions left: " + str(self.player1.MinorAction),
            style=1, 
            fg=(1,1,1,1), 
            pos=(0.2,-0.93), 
            scale = .08
        )
       
    def endTurn(self):
        print "THIS ENDS NOW!"
        for i,l in enumerate(map):
            for a,b in enumerate(l):
                if map[b.locx][b.locy].type == "N":
                    map[b.locx][b.locy].m.setTexture(self.normalTex,1)
        for i in self.turnList:
            i.StandardAction = 1
            i.MoveAction = 1
            i.MinorAction = 1
        self.updateGUI()
        self.rotateTurn()
        
    def startEnemyTurn(self,e,p):
        global map
        global adj
        #Find the closest square to the player and move there
        playeradj = False
        availableSquares = []
        finalChoices = []
        print e.StandardAction,e.MoveAction
        for i,d in adj:
            if e.locx + i == p.locx and e.locy + d == p.locy:
                playeradj = True
                 
        if playeradj == True:
            if e.StandardAction > 0:
                e.attack(p)
                print "Player is adjecent to me!"
                self.endTurn()
        else:
            print "Hmmm Player isn't adjecent...better move"
            if e.MoveAction > 0 or e.StandardAction > 0:
                for i,l in enumerate(map):
                    for a,b in enumerate(l):
                        if map[a][i].type == "N":
                            if p.locx != b.locx and p.locy != b.locy:
                                if sqrt((e.locx - b.locx)**2 + (e.locy - b.locy)**2) <= e.Speed:
                                    availableSquares.append(b)
                    
                for i in availableSquares:
                    for a in self.enemies:
                        if a.locx != i.locx and a.locy != i.locy:
                            i.distFPlayer = sqrt((i.locx - p.locx)**2 + (i.locy - p.locy)**2)
                            finalChoices.append(i)
                    
                toMove = finalChoices.sort()
                #print finalChoices
                if p.locx > e.locx and p.locy > e.locy:
                    toMove = finalChoices[-1]
                else:
                    toMove = finalChoices[0]
                    
                #finalChoices[6].m.setTexture(self.aSelTex, 1)
                e.move(toMove.locx,toMove.locy)
                    #self.endTurn()
                self.endTurn()
        
        
        
    def rotateTurn(self):
        se = Sequence(Wait(5))
        se.start()
        if not self.currentTurn + 1 <= len(self.turnList) - 1:
             self.currentTurn = 0
            
        else:
            self.currentTurn += 1
               
        if self.turnList[self.currentTurn] != self.player1:
            self.startEnemyTurn(self.turnList[self.currentTurn],self.player1)
        
        #print self.turnList[self.currentTurn]
    
    def create_map(self,x,y):
        global map
        map = [[space() for i in range(x)] for i in range (y)]
        for i,l in enumerate(map):
            for a,b in enumerate(l):
                b.m.reparentTo(self.onlyTiles)
                b.m.setPos(i * 5.318, a * 5.318, 0)
                b.m.setScale(0.1)
                b.m.setTag("Tile",str(a)+ "," + str(i))
                b.changePos(a,i)
                #self.tiles[str(a)] = b
            
    def createLevel1(self):
        global lvl1
        global map
        map = [[space() for i in range(25)] for i in range (25)]
        
        for i,l in enumerate(lvl1):
            for a,b in enumerate(l):
                map[a][i].type = b
                
        for i,l in enumerate(map):
            for a,b in enumerate(l):
                b.m.reparentTo(self.onlyTiles)
                b.m.setPos(i * 5.318, a * 5.318, 0)
                b.m.setScale(0.1)
                b.m.setTag("Tile",str(a)+ "," + str(i))
                b.changePos(a,i)
                #self.tiles[str(a)] = b
                if b.type == "E":
                    b.m.remove()
                if b.type == "B":
                    b.m.remove()
       
    def selSpace(self):
        times = 0
        global map
        global adj
        self.selSquare = self.hovSquare
        obj = self.selSquare 
        tagName = obj.getNetTag('Tile')
        split = tagName.split(",")
        x = int(split[1])
        y = int(split[0])#Reverse these
        #import pdb; pdb.set_trace()
        #print "-----------------------------------------------------------------"
        #for i,l in enumerate(map):
        #    for a,b in enumerate(l):
        #       sys.stdout.write(   "1" if map[b.locx][b.locy].canReach else "0")
        #    print "\n"
         
        if self.moveTurn == True:
            if self.selSquare != None and map[x][y].canReach:
                map[x][y].m.setTexture(self.normalTex,1)
                self.player1.move(x,y)
            self.moveTurn = False
        
        if self.attackTurn == True:
            if self.selSquare != None and map[x][y].canReach:
                if map[x][y].type == "N":
                    if self.player1.StandardAction > 0:
                        self.player1.attack(x,y,self.enemies)
                        map[x][y].m.setTexture(self.aSelTex,1)
                    self.attackTurn = False
                

            
        base.graphicsEngine.renderFrame()    
        
    def moveClicked(self):
        global map
        self.moveTurn = True
        self.attackTurn = False
        #import pdb; pdb.set_trace()
        for i,l in enumerate(map):
            for a,b in enumerate(l):
                if map[b.locx][b.locy].type == "N":
                    map[b.locx][b.locy].m.setTexture(self.normalTex,1)
                    
        if self.turnList[self.currentTurn] == self.player1:
            if self.player1.MoveAction > 0 or self.player1.StandardAction > 0:
                for i,l in enumerate(map):
                    for a,b in enumerate(l):
                        #b.m.setTexture(self.normalTex,1)
                        if map[b.locx][b.locy].type == "N":
                            if (sqrt((b.locx - self.player1.locx )**2 + (b.locy - self.player1.locy )**2 ) <= self.player1.Speed):
                                #import pdb; pdb.set_trace()
                                map[b.locx][b.locy].m.setTexture(self.mTex,1)
                                #b.m.setTexture(self.mTex,1)
                                map[b.locx][b.locy].canReach = True
                                #b.canReach = True
                            else:
                                map[b.locx][b.locy].canReach = False
                                #b.canReach = False
        
                    base.graphicsEngine.renderFrame()
        
    def attackClicked(self):
        global map
        self.moveTurn = False
        self.attackTurn = True
        for i,l in enumerate(map):
            for a,b in enumerate(l):
                if map[b.locx][b.locy].type == "N":
                    map[b.locx][b.locy].m.setTexture(self.normalTex,1)
                
        if self.turnList[self.currentTurn] == self.player1:
            if self.player1.StandardAction > 0:
                for x,y in adj:
                    if self.player1.locx + x >= 0 and self.player1.locy + y >= 0:
                        if map[self.player1.locx + x][self.player1.locy + y].type == "N":
                            map[self.player1.locx + x][self.player1.locy + y].m.setTexture(self.aTex,1)
                            map[self.player1.locx + x][self.player1.locy + y].canReach = True
                            
# Use map[b.locx][b.locy] instead of b.m!!!!!!!   
                
        
    def printLoc(self):
        obj = self.hovSquare #.findNetTag("Tile")
        rockName = obj.getNetTag('Tile') 
                
        realobj = map[int(rockName[0])][int(rockName[2])]
                
        print "HELLO"
        print realobj.locx, ",", realobj.locy
        print map[realobj.locx][realobj.locy].m.getTexture()
        #import pdb; pdb.set_trace()
    
    def mouseTask(self, task):
        
        self.selSquare = None
        if base.mouseWatcherNode.hasMouse():
          #get the mouse position
          mpos = base.mouseWatcherNode.getMouse()
          
          #Set the position of the ray based on the mouse position
          self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
          
          #If we are dragging something, set the position of the object
          #to be at the appropriate point over the plane of the board
           #Gets the point described by pickerRay.getOrigin(), which is relative to
        #camera, relative instead to render
        nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin())
            #Same thing with the direction of the ray
        nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection())
        
      #Do the actual collision pass (Do it only on the squares for
      #efficiency purposes)
        self.picker.traverse(self.onlyTiles)
        if self.pq.getNumEntries() > 0:
        #if we have hit something, sort the hits so that the closest
        #is first, and highlight that node
            self.pq.sortEntries()
            i = self.pq.getEntry(0).getIntoNodePath()
        #Set the highlight on the picked square
            self.hovSquare = i
        return Task.cont
        
        
def animStop(obj):
        obj.stop()
        
        
app = App()
run()